/*
 * Copyright 2010 WithOne Co. Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jp.co.withone.osgi.gadget.controller;

import java.awt.AWTException;
import java.awt.Font;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.ResourceBundle;

import javax.swing.UIManager;

import jp.co.withone.osgi.gadget.controller.preference.Preference;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.util.tracker.ServiceTracker;

/**
 * @author t.funakky
 */
public class ControllerCore implements BundleActivator, SynchronousBundleListener {

    private static final String MENU_ITEM_LABEL_EXIT = "popup.message.exit";

    private static final String MENU_ITEM_LABEL_SETTING = "popup.message.setting";

    public static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
            .getBundle("jp.co.withone.osgi.gadget.controller.resource.MessageResource");

    /** Tray Icon Resource Path */
    static final String RESOURCE_TRAY_ICON = "/jp/co/withone/osgi/gadget/controller/resource/trayIcon.png";

    private volatile TrayIcon trayIcon;

    private volatile BundleContext context;

    private volatile PreferenceManager preferenceManaer;

    private volatile ServiceTracker tracker;

    private volatile ServiceRegistration preferenceRegstration;

    private volatile List<BundleMenuItemWrapper> menuItemList;

    private volatile PopupMenu popupMenu;

    public void start(BundleContext context) {
        this.context = context;
        this.menuItemList = new ArrayList<BundleMenuItemWrapper>();

        Dictionary<String, Object> prop = new Hashtable<String, Object>();
        prop.put(Preference.NAME, RESOURCE_BUNDLE.getString(GadgetControlPreference.PREFERENCE_CONTROLLER_NAME));
        this.preferenceRegstration = context.registerService(Preference.class.getName(), new GadgetControlPreference(context), prop);

        this.preferenceManaer = new PreferenceManager(this.context);
        this.tracker = new ServiceTracker(context, Preference.class.getName(), this.preferenceManaer);
        this.tracker.open();

        context.addBundleListener(this);

        createTrayIcon();
    }

    public void stop(BundleContext context) {
        deleteTrayIcon();

        this.popupMenu = null;

        context.removeBundleListener(this);

        this.tracker.close();
        this.tracker = null;

        this.preferenceRegstration.unregister();
        this.preferenceRegstration = null;

        this.menuItemList = null;
        this.context = null;
    }

    private void createTrayIcon() {
        final SystemTray tray = SystemTray.getSystemTray();
        final URL imagePath = this.getClass().getResource(RESOURCE_TRAY_ICON);
        final Image image = Toolkit.getDefaultToolkit().getImage(imagePath);
        this.trayIcon = new TrayIcon(image);
        this.trayIcon.setImageAutoSize(true);
        this.trayIcon.setToolTip("Gadget on OSGi");
        this.trayIcon.setPopupMenu(getBundleMenuItems());
        try {
            tray.add(trayIcon);
        } catch (AWTException e1) {
            e1.printStackTrace();
        }
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void bundleChanged(BundleEvent event) {
        BundleMenuItemWrapper wrapper = getBundleMenuItemWrapper(event.getBundle());
        switch (event.getType()) {
        case BundleEvent.INSTALLED:
            setBundleMenuItem(event.getBundle());
            break;

        case BundleEvent.UNINSTALLED:
            this.popupMenu.remove(wrapper.menu);
            this.menuItemList.remove(wrapper);
            break;

        case BundleEvent.STARTED:
            wrapper.start.setEnabled(false);
            wrapper.stop.setEnabled(true);
            wrapper.menu.setFont(new Font(null, Font.PLAIN, 12));
            break;

        case BundleEvent.STOPPED:
            wrapper.start.setEnabled(true);
            wrapper.stop.setEnabled(false);
            wrapper.menu.setFont(new Font(null, Font.ITALIC, 12));
            break;

        default:
            break;
        }
    }

    private BundleMenuItemWrapper getBundleMenuItemWrapper(Bundle bundle) {
        for (BundleMenuItemWrapper wrapper : this.menuItemList) {
            if (wrapper.bundle == bundle) {
                return wrapper;
            }
        }
        return null;
    }

    private void deleteTrayIcon() {
        final SystemTray tray = SystemTray.getSystemTray();
        tray.remove(this.trayIcon);
        this.trayIcon = null;
    }

    private PopupMenu getBundleMenuItems() {
        this.popupMenu = new PopupMenu();
        popupMenu.addSeparator();
        popupMenu.add(getPreferenceView());
        popupMenu.addSeparator();
        popupMenu.add(getExitMenuItem());
        Bundle[] bundles = this.context.getBundles();
        for (Bundle bundle : bundles) {
            if (bundle.getBundleId() == 0 || bundle.equals(this.context.getBundle())) {
                continue;
            }
            //            if (bundle.getHeaders().get("Gadget-on-OSGi") == null) {
            //                continue;
            //            }
            setBundleMenuItem(bundle);
        }
        return popupMenu;
    }

    private void setBundleMenuItem(Bundle bundle) {
        String bundleName = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
        Menu menu = new Menu(bundleName);
        MenuItem start = getControlMenuItem(BundleActionCommand.START, bundle);
        if (bundle.getState() == Bundle.ACTIVE) {
            start.setEnabled(false);
            menu.setFont(new Font(null, Font.PLAIN, 12));
        }
        MenuItem stop = getControlMenuItem(BundleActionCommand.STOP, bundle);
        if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.INSTALLED) {
            stop.setEnabled(false);
            menu.setFont(new Font(null, Font.ITALIC, 12));
        }
        MenuItem uninstall = getControlMenuItem(BundleActionCommand.UNINSTALL, bundle);
        menu.add(start);
        menu.add(stop);
        menu.add(uninstall);
        BundleMenuItemWrapper menuItemWrapper = new BundleMenuItemWrapper(bundle, menu, start, stop, uninstall);
        this.popupMenu.insert(menuItemWrapper.menu, this.popupMenu.getItemCount() - 4);
        this.menuItemList.add(menuItemWrapper);
    }

    private MenuItem getControlMenuItem(BundleActionCommand actionCommand, Bundle bundle) {
        MenuItem menuItem = new MenuItem(RESOURCE_BUNDLE.getString(actionCommand.getPopupLabel()));
        menuItem.addActionListener(new BundleActionListenerImpl(bundle, actionCommand));
        menuItem.setActionCommand(actionCommand.toString());
        return menuItem;
    }

    private MenuItem getPreferenceView() {
        MenuItem menuItem = new MenuItem(RESOURCE_BUNDLE.getString(MENU_ITEM_LABEL_SETTING));
        menuItem.addActionListener(this.preferenceManaer);
        return menuItem;
    }

    private MenuItem getExitMenuItem() {
        MenuItem menuItem = new MenuItem(RESOURCE_BUNDLE.getString(MENU_ITEM_LABEL_EXIT));
        menuItem.addActionListener(new ExitActionListenerImpl(this.context.getBundle(0)));
        return menuItem;
    }

    private static enum BundleActionCommand {
        START("balloon.message.start", "popup.message.start"), STOP("balloon.message.stop", "popup.message.stop"), UNINSTALL(
                "balloon.message.uninstall", "popup.message.uninstall");

        //        private final String balloonLabel;

        private final String popupLabel;

        BundleActionCommand(String label, String popupLabel) {
            //            this.balloonLabel = label;
            this.popupLabel = popupLabel;
        }

        //        public String getBalloonLabel() {
        //            return balloonLabel;
        //        }

        String getPopupLabel() {
            return popupLabel;
        }
    }

    private static class BundleActionListenerImpl implements ActionListener {

        private final Bundle bundle;
        private final BundleActionCommand command;

        BundleActionListenerImpl(Bundle bundle, BundleActionCommand actionCommand) {
            this.bundle = bundle;
            this.command = actionCommand;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            try {
                switch (this.command) {
                case START:
                    this.bundle.start();
                    break;
                case STOP:
                    this.bundle.stop();
                    break;
                case UNINSTALL:
                    this.bundle.uninstall();
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static class BundleMenuItemWrapper {

        final Bundle bundle;
        final Menu menu;
        final MenuItem start;
        final MenuItem stop;
        final MenuItem uninstall;

        public BundleMenuItemWrapper(Bundle bundle, Menu menu, MenuItem start, MenuItem stop, MenuItem uninstall) {
            this.bundle = bundle;
            this.menu = menu;
            this.start = start;
            this.stop = stop;
            this.uninstall = uninstall;
        }
    }

    private static class ExitActionListenerImpl implements ActionListener {

        private final Bundle bundle;

        ExitActionListenerImpl(Bundle bundle) {
            this.bundle = bundle;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            try {
                this.bundle.stop();
            } catch (BundleException e) {
                e.printStackTrace();
                System.exit(1);
            }
            System.exit(0);
        }

    }
}
